package com.example.testdemo.spring;

import android.content.Context;

import com.example.testdemo.MainActivity;
import com.example.testdemo.annotation.Autowired;
import com.example.testdemo.annotation.BeanScan;
import com.example.testdemo.annotation.Component;
import com.example.testdemo.expection.BeansException;
import com.example.testdemo.spring.bean.BeanDefinition;
import com.example.testdemo.spring.bean.GenericBeanDefinition;
import com.example.testdemo.spring.config.ApplicationContextAware;
import com.example.testdemo.spring.config.BeanFactoryPostProcessor;
import com.example.testdemo.spring.config.InitializingBean;
import com.example.testdemo.spring.factory.DefaultBeanFactory;
import com.example.testdemo.spring.factory.SingletonBeanFactory;
import com.example.testdemo.spring.scan.ClassBeanScanner;
import com.example.testdemo.utils.StringUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author 永健
 * @since 2020-03-29 00:48
 */
public class ApplicationContext {

    private Context context;
    private MainActivity activity;

    private String packages;

    private ClassBeanScanner scanner;

    private DefaultBeanFactory beanFactory;
    private SingletonBeanFactory singletonBeanFactory;

    public ApplicationContext(MainActivity activity) {
        this.context = activity.getApplicationContext();
        handlerBeanScaner(activity);
    }


    private void handlerBeanScaner(MainActivity activity) {
        this.activity = activity;
        Class<? extends MainActivity> aClass = activity.getClass();
        BeanScan annotation = aClass.getAnnotation(BeanScan.class);
        if (StringUtils.isNotNull(annotation)) {
            this.packages = annotation.packages();
            this.refresh();
        }
    }

    private DefaultBeanFactory createBeanFactory() {
        DefaultBeanFactory beanFactory = new DefaultBeanFactory();
        this.singletonBeanFactory = beanFactory;
        return beanFactory;
    }

    private void refresh() {
        this.beanFactory = createBeanFactory();

        this.scanner = new ClassBeanScanner(beanFactory);

        this.scanner.scan(this.context, this.packages);

        invokeBeanFactoryPostProcessors(beanFactory);

        finishBeanInit();

        setApplicationContext();

        invokeInitializingBean(beanFactory);

    }

    private void setApplicationContext() {
        List<ApplicationContextAware> applicationContextAwares = beanFactory.getApplicationContextAwares();
        for (ApplicationContextAware contextAware : applicationContextAwares) {
            contextAware.setApplicationContext(this);
        }
    }

    private void invokeInitializingBean(DefaultBeanFactory beanFactory) {
        List<InitializingBean> initializingBeanList = beanFactory.getInitializingBean();
        for (InitializingBean initializingBean : initializingBeanList) {
            initializingBean.afterPropertiesSet();
        }
    }

    private void invokeBeanFactoryPostProcessors(DefaultBeanFactory beanFactory) {
        List<BeanFactoryPostProcessor> beanFactoryPostProcessor = beanFactory.getBeanFactoryPostProcessor();
        for (BeanFactoryPostProcessor processor : beanFactoryPostProcessor) {
            processor.postProcessBeanFactory(this.singletonBeanFactory);
        }
    }

    private void finishBeanInit() {
        Map<String, BeanDefinition> beanDefinitionMap = beanFactory.getBeanDefinitionMap();
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) entry.getValue();
            Class<?> beanClass = beanDefinition.getBeanClass();
            Object newInstance = createBean(beanClass);
            registerBean(beanName, newInstance);
        }
        // 处理依赖注入
        isInjection(beanDefinitionMap);
    }

    private void registerBean(Class<?> beanClass, String beanName) {
        Object newInstance = createBean(beanClass);

        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Autowired annotation = field.getAnnotation(Autowired.class);
            if (StringUtils.isNull(annotation)) {
                continue;
            }
            Object bean = getBean(beanName);
            if (StringUtils.isNotNull(bean)) {
                field.setAccessible(true);
                try {
                    field.set(newInstance, bean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                registerBean(beanName, newInstance);
            } else {
                Class<?> fieldType = field.getType();
                Component component = fieldType.getAnnotation(Component.class);
                if (StringUtils.isNull(component)) {
                    throw new BeansException("Autowired 注入必须不为空 " + field.getName());
                }

                // 递归查找

            }

        }
    }

    private void registerBean(String beanName, Object obj) {
        beanFactory.registerSingleton(beanName, obj);
    }

    private Object createBean(Class<?> beanClass) {
        Object newInstance = null;
        try {
            newInstance = beanClass.newInstance();

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return newInstance;
    }

    private void isInjection(Map<String, BeanDefinition> beanDefinitionMap) {

        // 再次遍历所有的实例化的 BeanDefinition,处理依赖关系
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition) entry.getValue();
            Class<?> beanClass = beanDefinition.getBeanClass();
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field field : declaredFields) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (StringUtils.isNull(annotation)) {
                    continue;
                }

                // 当前的实例
                Object bean = getBean(beanName);

                if (StringUtils.isNull(bean)) {
                    throw new BeansException("Autowired 注入必须不为空 " + field.getName());

                }
                // 允许操作修改
                field.setAccessible(true);
                Class<?> fieldType = field.getType();

                // 检查加加了  Autowired 注解的属性，是否加了 Component注解
                // 否则抛出异常，找不到实例注入
                Component component = fieldType.getAnnotation(Component.class);
                if (StringUtils.isNotNull(component)) {
                    String fieldBeanName = component.name();
                    if (StringUtils.isBlank(fieldBeanName)) {
                        // 如果加了，从IOC容器中获取当前属性的实例
                        fieldBeanName = toLowerCaseFirstOne(fieldType.getSimpleName());
                        Object fieldObject = getBean(fieldBeanName);
                        if (StringUtils.isNotNull(fieldObject)) {
                            try {
                                // 做属性赋值
                                field.set(bean, fieldObject);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }

    private static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }

    public <T> T getBean(String beanName) throws BeansException {
        return (T) beanFactory.getBean(beanName);
    }

    public Object getBean(Class<?> aClass) throws BeansException {
        return beanFactory.getBean(aClass);
    }


}
